package q.yazg;

import java.io.*;
import java.util.HashMap;
import java.util.Map;
import org.json.*;
import org.newdawn.slick.opengl.Texture;



/**
 *
 * @author khalisq
 */
public abstract class AnimatedEntity implements Entity, Listener{
    
    protected static final float DIRECTION_LEFT  = -1f,
                                 DIRECTION_RIGHT = 1f;
    
    protected AnimatedEntity(StateProvider sp){
        renderData = new HashMap();
        this.sp=sp;
        currentState = sp.getDefaultState();
    }
    
    protected AnimationState currentState;
    
    private long timeSinceLastFrame = 0;
    private int currentFrame;
    private Map<AnimationState, RenderData> renderData;
    private float texTileWidth, texTileHeight;
    private float w, h; //in pixels
    private Texture tex;
    private StateProvider sp;
    
    protected float x, y; //in meters
    protected float direction = DIRECTION_LEFT;
            
    public void message(Message msg){
        AnimationState nextState =currentState.message(msg);
        if(nextState!=currentState){
            timeSinceLastFrame=currentFrame=0;
            currentState=nextState;
        }        
    }
    
    public void update(long ticks){
        timeSinceLastFrame+=ticks;
        RenderData currRD = renderData.get(currentState);
        long duraofcurrfram = currRD.frames[currentFrame].duration;
        if(timeSinceLastFrame>currRD.frames[currentFrame].duration){
            currentFrame++;
            if(currentFrame>=currRD.frames.length)currentFrame=currRD.loop;
            timeSinceLastFrame-=duraofcurrfram;
        }
    }
    
    protected void parse(File f) throws JSONException, FileNotFoundException, IOException{
        StringBuilder sb = new StringBuilder();
        FileReader fr;
        BufferedReader read = new BufferedReader(fr = new FileReader(f));
        String line = read.readLine();
        while(line!=null){
            sb.append(line);
            line=read.readLine();
        }
        JSONObject obj = new JSONObject(sb.toString());
        
        tex = org.newdawn.slick.opengl.TextureLoader.getTexture("PNG", new FileInputStream(obj.getString("file")));
        w = (float)obj.getInt("tilew"); h = (float)obj.getInt("tileh");
        texTileWidth = w / ((float)tex.getTextureWidth()); texTileHeight = h / ((float)tex.getTextureHeight());
        
        JSONArray states = obj.getJSONObject("states").names();
        for(int i = 0; i<states.length(); i++){
            JSONObject state = obj.getJSONObject("states").getJSONObject(states.getString(i));
            if(!renderData.containsKey(sp.getState(states.getString(i))))
                renderData.put(sp.getState(states.getString(i)), new RenderData());
            RenderData rd = renderData.get(sp.getState(states.getString(i)));
            rd.loop = state.optInt("loop");
            
            JSONArray frames = state.getJSONArray("frames");
            rd.frames = new AnimationFrame[frames.length()];
            for(int j=0; j<rd.frames.length; j++){
                JSONObject frame = frames.getJSONObject(j);
                rd.frames[j]=new AnimationFrame(
                        ((float) frame.getDouble("x")) * texTileWidth,
                        ((float) frame.getDouble("y")) * texTileHeight,
                        frame.getLong("time")
                        );
            }
        }
    }
    
    public void render() {
        RenderData rd = renderData.get(currentState);
        Main.renderEntity(x, y, w, h, rd.frames[currentFrame].texX, rd.frames[currentFrame].texY, texTileWidth, texTileHeight, tex, direction == DIRECTION_LEFT);
    }
    
    protected void forceState(AnimationState state){
        currentFrame = 0;
        currentState = state;
    }
    interface StateProvider{
    
        public AnimationState getState(String name);
        
        public AnimationState getDefaultState();
    
    }
    
    protected interface AnimationState{
        
        public AnimationState message(Message msg);
        
    }
    
    private class RenderData{
        private AnimationFrame[] frames;
        private int loop;
    }
    
    private class AnimationFrame{
        
        private final float texX, texY;
        private final long duration;

        public AnimationFrame(float texX, float texY, long duration) {
            this.texX = texX;
            this.texY = texY;
            this.duration = duration;
        }        

        public long getDuration() {
            return duration;
        }

        public float getTexX() {
            return texX;
        }

        public float getTexY() {
            return texY;
        }
        
    }
    
    public float getX(){
        return x;
    }
    
    public float getY(){
        return y;
    }
    
    public float getW(){
        return w/Main.PPM;
    }
    
    public float getH(){
        return h/Main.PPM;
    }
}
